home *** CD-ROM | disk | FTP | other *** search
- /* MIXSD.C
- *
- * Miscellaneous helper functions common to all mixing sound devices.
- * Technically these functions should be part of each Sound Device's internal
- * code, but are here to save some space and help maintenance.
- *
- * $Id: mixsd.c,v 1.2 1997/01/16 18:41:59 pekangas Exp $
- *
- * Copyright 1996,1997 Housemarque Inc.
- *
- * This file is part of the MIDAS Sound System, and may only be
- * used, modified and distributed under the terms of the MIDAS
- * Sound System license, LICENSE.TXT. By continuing to use,
- * modify or distribute this file you indicate that you have
- * read the license and understand and accept it fully.
- */
-
- #include "lang.h"
- #include "mtypes.h"
- #include "errors.h"
- #include "mmem.h"
- #include "mixsd.h"
- #include "sdevice.h"
- #include "dsm.h"
- #include "dma.h"
-
- RCSID(const char *mixsd_rcsid = "$Id: mixsd.c,v 1.2 1997/01/16 18:41:59 pekangas Exp $";)
-
-
- //#define DUMPBUFFER
-
-
-
- #define MIX8BITS 12 /* number of bits of accuracy in
- mixing for 8-bit output */
-
- unsigned dmaBufferSize; /* DMA playback buffer size */
- //static dmaBuffer buffer; /* DMA playback buffer */
- dmaBuffer buffer;
- static unsigned mixRate; /* mixing rate */
- static unsigned outputMode; /* output mode */
- static unsigned amplification; /* amplification level */
- static unsigned updateMix; /* number of elements to mix between
- two updates */
- static unsigned mixLeft; /* number of elements to mix before
- next update */
- //static unsigned dmaPos; /* DMA position inside buffer */
- //static unsigned mixPos; /* mixing position */
- unsigned dmaPos, mixPos;
- int playDMA; /* Playing through DMA -flag */
- static uchar *ppTable; /* post-processing table for 8-bit
- output */
-
- #ifdef DUMPBUFFER
- #include <stdio.h>
- static FILE *f;
- #endif
-
-
-
-
- /****************************************************************************\
- *
- * Function: static unsigned CALLING (*postProc)(unsigned numElements,
- * uchar *bufStart, unsigned mixPos, unsigned *mixBuffer,
- * uchar *ppTable);
- *
- * Description: Pointer to the actual post-processing routine. Takes
- * DSM output elements from dsmMixBuffer and writes them to
- * DMA buffer at *bufStart in a format suitable for the Sound
- * Device.
- *
- * Input: unsigned numElements number of elements to process
- * (guaranteed to be even)
- * uchar *bufStart pointer to start of DMA buffer
- * unsigned mixPos mixing position in DMA buffer
- * unsigned *mixBuffer source mixing buffer
- * uchar *ppTable pointer to post-processing table
- *
- * Returns: New mixing position in DMA buffer. Can not fail.
- *
- \****************************************************************************/
-
- static unsigned CALLING (*postProc)(unsigned numElements, uchar *bufStart,
- unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
-
-
-
-
- /****************************************************************************\
- *
- * Function: unsigned pp16Mono();
- *
- * Description: 16-bit mono post-processing routine
- *
- \****************************************************************************/
-
- unsigned CALLING pp16Mono(unsigned numElements, uchar *bufStart,
- unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
-
-
-
-
- /****************************************************************************\
- *
- * Function: unsigned pp8Mono();
- *
- * Description: 8-bit mono post-processing routine
- *
- \****************************************************************************/
-
- unsigned CALLING pp8Mono(unsigned numElements, uchar *bufStart,
- unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
-
-
-
-
- /****************************************************************************\
- *
- * Function: unsigned pp16Stereo();
- *
- * Description: 16-bit stereo post-processing routine
- *
- \****************************************************************************/
-
- unsigned CALLING pp16Stereo(unsigned numElements, uchar *bufStart,
- unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
-
-
-
-
- /****************************************************************************\
- *
- * Function: unsigned pp8Stereo();
- *
- * Description: 8-bit stereo post-processing routine
- *
- \****************************************************************************/
-
- unsigned CALLING pp8Stereo(unsigned numElements, uchar *bufStart,
- unsigned mixPos, unsigned *mixBuffer, uchar *ppTable);
-
-
-
-
- /****************************************************************************\
- *
- * Function: int mixsdInit(unsigned mixRate, unsigned mode, unsigned
- * dmaChNum)
- *
- * Description: Common initialization for all mixing Sound Devices.
- * Initializes DMA functions, DSM, starts DMA playback and
- * allocates memory for possible post-processing tables
- *
- * Input: unsigned mixRate mixing rate in Hz
- * unsigned mode output mode
- * int dmaChNum DMA channel number / -1 if not to be
- * played yet
- *
- * Returns: MIDAS error code
- *
- \****************************************************************************/
-
- int CALLING mixsdInit(unsigned _mixRate, unsigned mode, int dmaChNum)
- {
- int error;
- int mixMode;
- U8 *p, val;
- unsigned i;
-
- mixRate = _mixRate;
- outputMode = mode;
- mixPos = 0;
-
- /* Calculate required DMA buffer size: (1/25th of a second) */
- dmaBufferSize = mixRate / DMABUFLEN;
- // dmaBufferSize = mixRate / 10;
-
- /* Multiply by 2 if stereo: */
- if ( mode & sdStereo )
- dmaBufferSize *= 2;
-
- /* Multiply by 2 if 16-bit: */
- if ( mode & sd16bit )
- dmaBufferSize *= 2;
-
- /* Make buffer length a multiple of 16: */
- dmaBufferSize = (dmaBufferSize + 15) & 0xFFF0;
-
- /* Point postProc() to correct post-processing routine: */
- switch ( mode )
- {
- case (sd16bit | sdMono):
- postProc = &pp16Mono;
- break;
-
- case (sd8bit | sdMono):
- postProc = &pp8Mono;
- break;
-
- case (sd16bit | sdStereo):
- postProc = &pp16Stereo;
- break;
-
- case (sd8bit | sdStereo):
- postProc = &pp8Stereo;
- break;
-
- default:
- ERROR(errInvalidArguments, ID_mixsdInit);
- return errInvalidArguments;
- }
-
- /* Allocate DMA buffer: */
- if ( (error = dmaAllocBuffer(dmaBufferSize, &buffer)) != OK )
- PASSERROR(ID_mixsdInit)
-
- /* Now clear the DMA buffer to avoid nasty clicks */
-
- /* Check zero value: */
- if ( mode & sd8bit )
- val = 0x80;
- else
- val = 0;
-
- /* And clear it: */
- for ( p = (U8*) buffer.dataPtr, i = 0; i < dmaBufferSize; i++,
- *(p++) = val );
-
- /* Allocate memory for post-processing table if necessary: */
- if ( mode & sd8bit )
- {
- /* Allocate memory for 8-bit output mode post-processing table: */
- if ( (error = memAlloc((1 << MIX8BITS), (void**) &ppTable)) != OK )
- PASSERROR(ID_mixsdInit);
- }
- else
- ppTable = NULL;
-
- /* Check correct mixing mode: */
- if ( mode & sdStereo )
- mixMode = dsmMixStereo;
- else
- mixMode = dsmMixMono;
-
- /* Initialize Digital Sound Mixer: */
- if ( mode & sd16bit )
- {
- if ( (error = dsmInit(mixRate, mixMode, 16)) != OK )
- PASSERROR(ID_mixsdInit)
- }
- else
- {
- if ( (error = dsmInit(mixRate, mixMode, MIX8BITS)) != OK )
- PASSERROR(ID_mixsdInit)
- }
-
-
- if ( dmaChNum != -1 )
- {
- /* Start playing the DMA buffer: */
- if ( (error = dmaPlayBuffer(&buffer, dmaChNum, 1)) != OK )
- PASSERROR(ID_mixsdInit)
- playDMA = 1;
- }
- else
- {
- playDMA = 0;
- }
-
- /* Set update rate to 50Hz: */
- if ( (error = mixsdSetUpdRate(5000)) != OK )
- PASSERROR(ID_mixsdInit)
-
- #ifdef DUMPBUFFER
- f = fopen("dmabuffer.smp", "wb");
- #endif
-
- return OK;
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: int CALLING mixsdClose(void)
- *
- * Description: Common uninitialization code for all mixing Sound Devices.
- * Uninitializes DMA playback and DSM and deallocates memory.
- *
- * Returns: MIDAS error code
- *
- \****************************************************************************/
-
- int CALLING mixsdClose(void)
- {
- int error;
-
- if ( playDMA == 1 )
- {
- /* Stop DMA playback: */
- if ( (error = dmaStop(buffer.channel)) != OK )
- PASSERROR(ID_mixsdClose)
- }
-
- /* Uninitialize Digital Sound Mixer: */
- if ( (error = dsmClose()) != OK )
- PASSERROR(ID_mixsdClose)
-
- /* Deallocate DMA buffer: */
- if ( (error = dmaFreeBuffer(&buffer)) != OK )
- PASSERROR(ID_mixsdClose)
-
- /* Deallocate post-processing table if necessary: */
- if ( outputMode & sd8bit )
- {
- if ( (error = memFree(ppTable)) != OK )
- PASSERROR(ID_mixsdClose);
- }
-
- #ifdef DUMPBUFFER
- fclose(f);
- #endif
-
- return OK;
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: int mixsdGetMode(unsigned *mode)
- *
- * Description: Reads the current output mode
- *
- * Input: unsigned *mode pointer to output mode
- *
- * Returns: MIDAS error code. Output mode is written to *mode.
- *
- \****************************************************************************/
-
- int CALLING mixsdGetMode(unsigned *mode)
- {
- *mode = outputMode;
-
- return OK;
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: int mixsdOpenChannels(unsigned channels)
- *
- * Description: Opens sound channels for output. Prepares post-processing
- * tables, takes care of default amplification and finally opens
- * DSM channels. Channels can be closed by simply calling
- * dsmCloseChannels().
- *
- * Input: unsigned channels number of channels to open
- *
- * Returns: MIDAS error code
- *
- \****************************************************************************/
-
- int CALLING mixsdOpenChannels(unsigned channels)
- {
- int error;
-
- /* Open DSM channels: */
- if ( (error = dsmOpenChannels(channels)) != OK )
- PASSERROR(ID_mixsdOpenChannels)
-
- /* Take care of default amplification and calculate new post-processing
- table if necessary: */
- if ( channels < 5 )
- mixsdSetAmplification(64);
- else
- mixsdSetAmplification(14*channels);
-
- return OK;
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: void CalcPP8Table(void)
- *
- * Description: Calculates a new 8-bit output post-processing table using
- * current amplification level
- *
- \****************************************************************************/
-
- static void CalcPP8Table(void)
- {
- uchar *tbl;
- int val;
- long temp;
-
- tbl = ppTable; /* tbl points to current table pos */
-
- /* Calculate post-processing table for all possible DSM values:
- (table must be used with unsigned numbers - add (1 << MIX8BITS)/2 to
- DSM output values first) */
- for ( val = -(1 << MIX8BITS)/2; val < (1 << MIX8BITS)/2; val++ )
- {
- /* Calculate 8-bit unsigned output value corresponding to the
- current DSM output value (val), taking amplification into
- account: */
- temp = 128 + ((((long) amplification) * ((long) val) / 64L) >>
- (MIX8BITS-8));
-
- /* Clip the value to fit between 0 and 255 inclusive: */
- if ( temp < 0 )
- temp = 0;
- if ( temp > 255 )
- temp = 255;
-
- /* Write the value to the post-processing table: */
- *(tbl++) = (uchar) temp;
- }
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: int mixsdSetAmplification(unsigned amplification)
- *
- * Description: Sets the amplification level. Calculates new post-processing
- * tables and calls dsmSetAmplification() as necessary.
- *
- * Input: unsigned amplification amplification value
- *
- * Returns: MIDAS error code
- *
- \****************************************************************************/
-
- int CALLING mixsdSetAmplification(unsigned _amplification)
- {
- int error;
-
- amplification = _amplification;
-
- if ( outputMode & sd8bit )
- {
- /* 8-bit output mode - do not set amplification level using DSM,
- but calculate a new post-processing table instead: */
- CalcPP8Table();
- }
- else
- {
- /* Set amplification level to DSM: */
- if ( (error = dsmSetAmplification(amplification)) != OK )
- PASSERROR(ID_mixsdSetAmplification)
- }
-
- return OK;
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: int mixsdGetAmplification(unsigned *amplification);
- *
- * Description: Reads the current amplification level. (DSM doesn't
- * necessarily know the actual amplification level if
- * post-processing takes care of amplification)
- *
- * Input: unsigned *amplification pointer to amplification level
- *
- * Returns: MIDAS error code. Amplification level is written to
- * *amplification.
- *
- \****************************************************************************/
-
- int CALLING mixsdGetAmplification(unsigned *_amplification)
- {
- *_amplification = amplification;
-
- return OK;
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: int mixsdSetUpdRate(unsigned updRate);
- *
- * Description: Sets the channel value update rate (depends on song tempo)
- *
- * Input: unsigned updRate update rate in 100*Hz (eg. 50Hz
- * becomes 5000).
- *
- * Returns: MIDAS error code
- *
- \****************************************************************************/
-
- int CALLING mixsdSetUpdRate(unsigned updRate)
- {
- /* Calculate number of elements to mix between two updates: (even) */
- mixLeft = updateMix = ((unsigned) ((100L * (ulong) mixRate) /
- ((ulong) updRate)) + 1) & 0xFFFFFFFE;
-
- return OK;
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: int mixsdStartPlay(void)
- *
- * Description: Prepares for playing - reads DMA playing position. Called
- * once before the Sound Device and music player polling loop.
- *
- * Returns: MIDAS error code
- *
- \****************************************************************************/
-
- int CALLING mixsdStartPlay(void)
- {
- int error;
-
- /* Read DMA playing position: */
- if ( (error = dmaGetPos(&buffer, &dmaPos)) != OK )
- PASSERROR(ID_mixsdStartPlay)
-
- return OK;
- }
-
-
-
-
- /****************************************************************************\
- *
- * Function: int mixsdPlay(int *callMP);
- *
- * Description: Plays the sound - mixes the correct amount of data with DSM
- * and copies it to DMA buffer with post-processing.
- *
- * Input: int *callMP pointer to music player calling flag
- *
- * Returns: MIDAS error code. If enough data was mixed for one updating
- * round and music player should be called, 1 is written to
- * *callMP, otherwise 0 is written there. Note that if music
- * player can be called, mixsdPlay() should be called again
- * with a new check for music playing to ensure the DMA buffer
- * gets filled with new data.
- *
- \****************************************************************************/
-
- int CALLING mixsdPlay(int *callMP)
- {
- unsigned *mixBuf;
- int error;
- int numElems, mixNow;
- int dsmBufSize;
- int dmaBufLeft;
- #ifdef DUMPBUFFER
- unsigned oldPos;
- #endif
-
- /* Calculate number of bytes that fits in the buffer: */
- if ( dmaPos >= mixPos )
- numElems = dmaPos - mixPos - 16;
- else
- numElems = dmaBufferSize - mixPos + dmaPos - 16;
-
- /* Do not mix less than 16 bytes: */
- if ( numElems < 16 )
- {
- *callMP = 0;
- return OK;
- }
-
- /* Calculate actual number of elements: */
- if ( outputMode & sdStereo )
- numElems >>= 1;
- if ( outputMode & sd16bit )
- numElems >>= 1;
-
- /* Make sure number of elements is even: */
- numElems = numElems & 0xFFFFFFFE;
-
- /* Check that we won't mix more elements than there is left before next
- update: */
- if ( numElems > mixLeft )
- numElems = mixLeft;
-
- /* Calculate DSM mixing buffer size in elements: (FIXME) */
- dsmBufSize = dsmMixBufferSize;
- #ifdef __32__
- dsmBufSize >>= 2;
- #else
- dsmBufSize >>= 1;
- #endif
- if ( outputMode & sdStereo )
- dsmBufSize >>= 1;
-
- /* Decrease number of elements before next update: */
- mixLeft -= numElems;
-
- /* Mix all the data and post-process it to the DMA buffer, making sure
- DSM mixing buffer will not overflow: */
- while ( numElems )
- {
- /* mixNow = number of elements to mix in this round: */
- if ( numElems > dsmBufSize )
- mixNow = dsmBufSize;
- else
- mixNow = numElems;
-
- /* Decrease number of elements left: */
- numElems -= mixNow;
-
- /* Mix the data: */
- if ( (error = dsmMixData(mixNow)) != OK )
- PASSERROR(ID_mixsdPlay)
-
- /* Point mixBuf to current mixing buffer position: */
- mixBuf = dsmMixBuffer;
-
- /* Calculate the number of elements left in DMA buffer before
- buffer end: */
- dmaBufLeft = dmaBufferSize - mixPos;
- if ( outputMode & sdStereo )
- dmaBufLeft >>= 1;
- if ( outputMode & sd16bit )
- dmaBufLeft >>= 1;
-
- /* If DMA buffer end would be reached, first process the data up
- to the buffer end: */
- if ( dmaBufLeft < mixNow )
- {
- #ifdef DUMPBUFFER
- oldPos = mixPos;
- #endif
- postProc(dmaBufLeft, (uchar*) buffer.dataPtr, mixPos, mixBuf,
- ppTable);
- #ifdef DUMPBUFFER
- fwrite(((uchar*) buffer.dataPtr) + oldPos, dmaBufLeft, 1, f);
- #endif
- mixNow -= dmaBufLeft;
- mixPos = 0;
- if ( outputMode & sdStereo )
- mixBuf += 2*dmaBufLeft;
- else
- mixBuf += dmaBufLeft;
- }
-
- /* Process the rest of the data and update mixing position: */
- #ifdef DUMPBUFFER
- oldPos = mixPos;
- #endif
- mixPos = postProc(mixNow, (uchar*) buffer.dataPtr, mixPos, mixBuf,
- ppTable);
- #ifdef DUMPBUFFER
- fwrite(((uchar*) buffer.dataPtr) + oldPos, mixNow, 1, f);
- #endif
-
- if ( mixPos >= dmaBufferSize )
- mixPos = 0;
- }
-
- /* Check if music player should be called: */
- if ( mixLeft == 0 )
- {
- mixLeft = updateMix;
- *callMP = 1;
- }
- else
- {
- *callMP = 0;
- }
-
- return OK;
- }
-
-
- /*
- * $Log: mixsd.c,v $
- * Revision 1.2 1997/01/16 18:41:59 pekangas
- * Changed copyright messages to Housemarque
- *
- * Revision 1.1 1996/05/22 20:49:33 pekangas
- * Initial revision
- *
- */